#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>     // 用于 fork, execvp, pipe, dup2, chdir, getcwd, gethostname, getuid, STDIN_FILENO, STDOUT_FILENO
#include <sys/types.h>  // 用于 pid_t, uid_t
#include <sys/wait.h>   // 用于 waitpid
#include <pwd.h>        // 用于 getpwuid, struct passwd
#include <errno.h>      // 用于 perror
#include <ctype.h>      // 用于 isspace

#define MAX_PATH_LEN 256
#define MAX_HOSTNAME_LEN 100
#define MAX_INPUT_LEN 1024
#define MAX_ARGS 64         // 单个命令段的最大参数数量
#define MAX_COMMANDS 10     // 管道链中最大命令数量

// STDIN_FILENO 是 0, STDOUT_FILENO 是 1, STDERR_FILENO 是 2

// 函数：显示Shell提示符
void display_prompt() {
    char hostname[MAX_HOSTNAME_LEN];
    char current_path[MAX_PATH_LEN];
    struct passwd *pw_entry;
    uid_t user_id;

    if (gethostname(hostname, sizeof(hostname)) == -1) {
        perror("gethostname 错误");
        strcpy(hostname, "unknown_host");
    }

    if (getcwd(current_path, sizeof(current_path)) == NULL) {
        perror("getcwd 错误");
        strcpy(current_path, "/unknown_path");
    }

    user_id = getuid();
    pw_entry = getpwuid(user_id);
    char *username = "unknown_user";
    if (pw_entry != NULL) {
        username = pw_entry->pw_name;
    }

    printf("%s@%s:%s$ ", username, hostname, current_path);
    fflush(stdout); // 确保在 fgets 前显示提示符
}

// 函数：将不包含'|'的单个命令字符串解析成argv格式 (使用 strtok_r)
int parse_command_segment(char *command_str, char *argv[]) {
    int argc = 0;
    char *token;
    const char *delimiters = " \t\n"; // 空格、制表符、换行符
    char *saveptr_args; // 用于 strtok_r 按参数分割

    if (command_str == NULL) {
        argv[0] = NULL;
        return 0;
    }

    token = strtok_r(command_str, delimiters, &saveptr_args);
    while (token != NULL && argc < MAX_ARGS - 1) {
        argv[argc++] = token;
        token = strtok_r(NULL, delimiters, &saveptr_args);
    }
    argv[argc] = NULL; // 以NULL结尾参数列表
    return argc;
}

// 函数：执行内置命令
// 如果是内置命令并已处理，返回1，否则返回0
int execute_builtin_command(char *argv[], int argc) {
    if (argc == 0 || argv[0] == NULL) return 0;

    if (strcmp(argv[0], "exit") == 0) {
        printf("Shell 退出。\n");
        exit(EXIT_SUCCESS);
        return 1;
    } else if (strcmp(argv[0], "cd") == 0) {
        char *path_to_cd;
        if (argc < 2 || argv[1] == NULL || strlen(argv[1]) == 0) {
            path_to_cd = getenv("HOME");
            if (path_to_cd == NULL) {
                fprintf(stderr, "cd: HOME 环境变量未设置。\n");
                return 1;
            }
        } else {
            path_to_cd = argv[1];
        }
        if (chdir(path_to_cd) != 0) {
            perror("cd 错误");
        }
        return 1;
    } else if (strcmp(argv[0], "help") == 0) {
        printf("一个简单的 Shell 程序 (支持多重管道)\n");
        printf("支持的内置命令:\n");
        printf("  cd [目录]    - 更改当前工作目录\n");
        printf("  exit          - 退出 Shell\n");
        printf("  help          - 显示此帮助信息\n");
        printf("输入命令，用 '|' 分隔以创建管道。\n");
        return 1;
    }
    return 0;
}

// 函数：执行单个外部命令（此处不直接涉及管道）
void execute_external_command(char *argv[]) {
    if (argv == NULL || argv[0] == NULL) {
        return;
    }

    pid_t pid = fork();
    if (pid < 0) {
        perror("fork 错误");
        return;
    } else if (pid == 0) {
        if (execvp(argv[0], argv) == -1) {
            fprintf(stderr, "%s: 命令未找到或执行错误\n", argv[0]);
            perror("execvp 执行失败");
            exit(EXIT_FAILURE);
        }
    } else {
        int status;
        if (waitpid(pid, &status, 0) == -1) {
            perror("waitpid 错误");
        }
    }
}


// 函数：执行一个包含一个或多个命令的管道链
void execute_pipeline(char *commands_argv_list[][MAX_ARGS], int num_commands_in_pipeline) {
    if (num_commands_in_pipeline <= 0) {
        return;
    }

    if (num_commands_in_pipeline == 1) {
        int single_cmd_argc = 0;
        while(commands_argv_list[0][single_cmd_argc] != NULL && single_cmd_argc < MAX_ARGS) {
            single_cmd_argc++;
        }
        if (commands_argv_list[0][0] == NULL) { // 检查解析后是否为空命令
            // fprintf(stderr, "错误：尝试执行空命令。\n"); // 可选
            return;
        }
        if (!execute_builtin_command(commands_argv_list[0], single_cmd_argc)) {
            execute_external_command(commands_argv_list[0]);
        }
        return;
    }

    int pipe_fds[2];
    int prev_pipe_read_end = STDIN_FILENO;
    pid_t pids[MAX_COMMANDS]; // 假设 num_commands_in_pipeline <= MAX_COMMANDS
    for(int i=0; i < num_commands_in_pipeline; ++i) pids[i] = 0; // 初始化为0，标记为无效PID

    for (int i = 0; i < num_commands_in_pipeline; i++) {
        if (i < num_commands_in_pipeline - 1) {
            if (pipe(pipe_fds) == -1) {
                perror("pipe 创建失败");
                for (int k = 0; k < i; k++) if(pids[k] > 0) waitpid(pids[k], NULL, 0);
                if(prev_pipe_read_end != STDIN_FILENO) close(prev_pipe_read_end);
                return;
            }
        }

        pids[i] = fork();
        if (pids[i] < 0) {
            perror("fork 失败");
            if (i < num_commands_in_pipeline - 1) {
                close(pipe_fds[0]);
                close(pipe_fds[1]);
            }
            if (prev_pipe_read_end != STDIN_FILENO) close(prev_pipe_read_end);
            for (int k = 0; k < i; k++) if(pids[k] > 0) waitpid(pids[k], NULL, 0);
            return;
        }

        if (pids[i] == 0) { // 子进程
            if (i > 0) {
                if (dup2(prev_pipe_read_end, STDIN_FILENO) == -1) {
                    perror("子进程: dup2 (stdin) 失败");
                    exit(EXIT_FAILURE);
                }
                close(prev_pipe_read_end);
            }

            if (i < num_commands_in_pipeline - 1) {
                close(pipe_fds[0]);
                if (dup2(pipe_fds[1], STDOUT_FILENO) == -1) {
                    perror("子进程: dup2 (stdout) 失败");
                    exit(EXIT_FAILURE);
                }
                close(pipe_fds[1]);
            }

            int current_cmd_argc = 0;
            while(commands_argv_list[i][current_cmd_argc] != NULL && current_cmd_argc < MAX_ARGS) {
                current_cmd_argc++;
            }
            if (execute_builtin_command(commands_argv_list[i], current_cmd_argc)) {
                 exit(EXIT_SUCCESS);
            }

            if (execvp(commands_argv_list[i][0], commands_argv_list[i]) == -1) {
                fprintf(stderr, "%s: 命令未找到或执行错误 (子进程 PID %d)\n", commands_argv_list[i][0], getpid());
                perror("子进程: execvp 执行失败");
                exit(EXIT_FAILURE);
            }
        }

        // 父进程
        if (prev_pipe_read_end != STDIN_FILENO) {
            close(prev_pipe_read_end);
        }
        if (i < num_commands_in_pipeline - 1) {
            close(pipe_fds[1]);
            prev_pipe_read_end = pipe_fds[0];
        }
    }

    for (int i = 0; i < num_commands_in_pipeline; i++) {
        int status;
        if (pids[i] > 0) {
             waitpid(pids[i], &status, 0);
        }
    }
}


// Shell主循环
int main() {
    char input_buffer_original[MAX_INPUT_LEN]; // 保存原始 fgets 输入
    char input_copy_for_pipe_parsing[MAX_INPUT_LEN]; // 用于 strtok_r 按'|'分割的副本

    char *pipe_segments_str[MAX_COMMANDS];
    char *argv_for_each_segment[MAX_COMMANDS][MAX_ARGS];

    while (1) {
        display_prompt();

        // 清理上一次命令的解析结果
        for(int i=0; i<MAX_COMMANDS; ++i) {
            pipe_segments_str[i] = NULL;
            for(int j=0; j<MAX_ARGS; ++j) {
                argv_for_each_segment[i][j] = NULL;
            }
        }

        if (fgets(input_buffer_original, sizeof(input_buffer_original), stdin) == NULL) {
            printf("\nShell 退出。\n");
            break;
        }
        
        strncpy(input_copy_for_pipe_parsing, input_buffer_original, MAX_INPUT_LEN -1);
        input_copy_for_pipe_parsing[MAX_INPUT_LEN-1] = '\0';
        input_copy_for_pipe_parsing[strcspn(input_copy_for_pipe_parsing, "\n")] = 0; // 移除换行符


        if (strlen(input_copy_for_pipe_parsing) == 0) {
            continue;
        }

        int num_parsed_commands = 0;
        char *saveptr_pipe;
        char *current_command_string = strtok_r(input_copy_for_pipe_parsing, "|", &saveptr_pipe);
        int syntax_error_in_pipe = 0;
        
        while (current_command_string != NULL && num_parsed_commands < MAX_COMMANDS) {
            char *trimmed_command = current_command_string;
            // 去除前导空格
            while (isspace((unsigned char)*trimmed_command)) {
                trimmed_command++;
            }
            // 去除尾随空格
            char *end = trimmed_command + strlen(trimmed_command);
            while (end > trimmed_command && isspace((unsigned char)*(end - 1))) {
                end--;
            }
            *end = '\0';

            if (strlen(trimmed_command) > 0) {
                pipe_segments_str[num_parsed_commands++] = trimmed_command;
            } else {
                // 如果trim后为空，并且这不是第一个命令段（即前面有有效命令和'|'）
                // 或者后面还有'|'（意味着这是一个空的中间段 "cmd1 | | cmd2"）
                // 这被视为语法错误
                fprintf(stderr, "错误: 管道语法错误，出现空命令段。\n");
                syntax_error_in_pipe = 1;
                break; 
            }
            current_command_string = strtok_r(NULL, "|", &saveptr_pipe);
        }

        if (syntax_error_in_pipe) {
            continue; // 或者 goto next_command_loop;
        }
        
        // 如果分割后没有命令 (例如输入只有"|", 或者 " | | ")
        if (num_parsed_commands == 0) {
            if (strchr(input_buffer_original, '|') != NULL) { // 原始输入包含'|'但解析不出命令
                 fprintf(stderr, "错误: 无效的管道命令格式。\n");
            } else { // 没有'|'，但可能就是空行或只有空格（上面strlen应该捕获了空行）
                // 如果是 "   "，上面strlen后这里num_parsed_commands可能是0，是合理的continue
            }
            continue;
        }
        
        // 现在，将每个命令段解析为其自己的argv
        int parse_error_segment = 0;
        for (int i = 0; i < num_parsed_commands; i++) {
            // pipe_segments_str[i] 指向 input_copy_for_pipe_parsing 中的特定（已trim的）子串
            // parse_command_segment 会修改这个子串（因为它内部用strtok_r）
            // 所以这里直接传递 pipe_segments_str[i] 是可以的，因为它已经是 "干净" 的准备被空格分割的字符串了
            parse_command_segment(pipe_segments_str[i], argv_for_each_segment[i]);
            
            if (argv_for_each_segment[i][0] == NULL) {
                fprintf(stderr, "错误: 管道中的第 %d 个命令段解析后为空命令。\n", i + 1);
                parse_error_segment = 1;
                break;
            }
        }
        
        if (parse_error_segment) {
            continue;
        }

        execute_pipeline(argv_for_each_segment, num_parsed_commands);
    }
    return 0;
}
